package tool

import (
	"bytes"
	"fmt"
	"leaf/core/log"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
)

var _gocount int32 = 0
var _gowait sync.WaitGroup
var _goSigChan = make(chan bool, 1)
var _goClose int32 = 1
var _goDebug = false

func GoSigChan() chan bool {
	return _goSigChan
}

func SetGoDebug(b bool) {
	_goDebug = b
}

func GoClose() {
	close(_goSigChan)
	atomic.CompareAndSwapInt32(&_goClose, 1, 0)
}

func IsStop() bool {
	return _goClose == 0
}

func IsRuning() bool {
	return _goClose == 1
}

func GoWait() {
	_gowait.Wait()
}

//	func Go(f func()) {
//		GoTry(f, nil)
//	}
func LogSimpleStack() string {
	_, file, line, _ := runtime.Caller(2)
	i := strings.LastIndex(file, "/") + 1
	i = strings.LastIndex((string)(([]byte(file))[:i-1]), "/") + 1

	return fmt.Sprintf("%s:%d", (string)(([]byte(file))[i:]), line)
}

func GetGoId() uint64 {
	b := make([]byte, 64)
	b = b[:runtime.Stack(b, false)]
	b = bytes.TrimPrefix(b, []byte("goroutine "))
	b = b[:bytes.IndexByte(b, ' ')]
	n, _ := strconv.ParseUint(string(b), 10, 64)
	return n
}

func Go(f func()) {
	_gowait.Add(1)
	gocount := atomic.AddInt32(&_gocount, 1)
	var debugStr = LogSimpleStack()

	go func() {
		if _goDebug {
			log.Debug("goroutine start goid:%v gocount:%v stack:%v", GetGoId(), gocount, debugStr)
		}

		Try(f, nil)
		gocount = atomic.AddInt32(&_gocount, -1)
		if _goDebug {
			log.Debug("goroutine over goid:%v gocount:%v stack:%v", GetGoId(), gocount, debugStr)
		}

		_gowait.Add(-1)
	}()
}

func GoTry(f func(), handler func(interface{})) {
	_gowait.Add(1)
	gocount := atomic.AddInt32(&_gocount, 1)
	log.Debug("goroutine start gocount:%v", gocount)
	go func() {
		Try(f, handler)
		gocount := atomic.AddInt32(&_gocount, -1)
		log.Debug("goroutine over gocount:%v", gocount)
		_gowait.Add(-1)
	}()
}

func Try(fun func(), handler func(interface{})) {
	defer func() {
		if r := recover(); r != nil {
			if handler == nil {
				buf := make([]byte, 4096)
				l := runtime.Stack(buf, true)
				err := fmt.Errorf("%v: %s", r, buf[:l])
				log.Error("error stack:%v \n", err)
			} else {
				handler(r)
			}
		}
	}()
	fun()
}
